package com.leo.foodmaster.commons.aspect;

import com.leo.foodmaster.admin.system.entity.UserBean;
import com.leo.foodmaster.commons.util.WebUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>
 * 网络请求日志切面
 * </p>
 *
 * @author ：Leo
 * @since ：2021-03-10 9:31
 */
@Aspect
@Component
@Profile("dev")
public class WebLogAspect {
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 定义切面表达式
     * 以 controller 包下定义的所有请求为切入点
     */
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void webLog(){
        // Do nothing because of pointcut expression.
    }


    /**
     * 再切点之前植入
     * @param joinPoint 切点
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes, "ServletRequestAttributes is null !");
        HttpServletRequest request = attributes.getRequest();
        String uri = request.getRequestURI();
        UserBean user = WebUtils.getUser();
        String account;
        if (user == null) {
            account = "anonymous";
        }else {
            account = user.getAccount();
        }
        redisTemplate.opsForList().leftPush("sql-" + uri + "-" + account, "");

        String classReference = joinPoint.getSignature().getDeclaringTypeName().replace(".", "\\");
        String path = System.getProperty("user.dir")+"\\src\\main\\java\\" + classReference + ".java";
        File file = new File(path);
        String methodName = joinPoint.getSignature().getName();
        int rowNum = 1;
        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
            //构造一个BufferedReader类来读取文件
            String s;
            //使用readLine方法，一次读一行
            while ((s = br.readLine()) != null) {
                if (s.contains(methodName + "(")) {
                    break;
                }
                rowNum++;
            }
        } catch (Exception e) {
            System.err.println("已经打包,未读取到源码");
        }

        // 每个请求之间空一行
        System.err.println("\r\n \r\n");
        // 打印请求相关参数
        System.err.println("========================================== Start ==========================================");
        System.err.println("目 标 类：    ->\t " + joinPoint.getSignature().getDeclaringTypeName()
                + ".(" + joinPoint.getSignature().getDeclaringType().getSimpleName() + ".java:" + rowNum + ")");
        System.err.println("目标方法：    ->\t " + methodName+"( )");
        Map<String, String[]> parameterMap = request.getParameterMap();
        Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet();
        for (Map.Entry<String, String[]> entry : entrySet) {
            try {
                System.err.println("请求参数：    ->\t " + entry.getKey() + " = " + entry.getValue()[0]);
            } catch (ArrayIndexOutOfBoundsException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 环绕
     * @param proceedingJoinPoint proceedingJoinPoint
     * @return Object
     * @throws Throwable throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes, "ServletRequestAttributes is null !");
        HttpServletRequest request = attributes.getRequest();
        String uri = request.getRequestURI();
        UserBean user = WebUtils.getUser();
        String account;
        if (user == null) {
            account = "anonymous";
        }else {
            account = user.getAccount();
        }
        List<String> o = redisTemplate.opsForList().range("sql-" + uri + "-" + account, 0, -1);
        assert o != null;
        for (String s : o) {
            System.err.println(s);
        }
        redisTemplate.delete("sql-" + uri + "-" + account);
        // 执行耗时
        System.err.println("执行耗时：    ->\t " + (System.currentTimeMillis() - startTime) + "ms");

        System.err.println("=========================================== End ===========================================");
        return result;
    }
}
